fix: rebuild index when mode adds capabilities - #1263
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes incremental routing decisions across index mode transitions by persisting the effective index capability level in Project metadata (properties.index_mode), forcing a full rebuild when the requested mode requires capabilities not present in the stored index, and preserving stronger stored capabilities during downgrades (while still honoring the requested mode for discovery/exclusions). It also ensures persistent artifacts are refreshed after reindexing and adds regression coverage for mode upgrades/downgrades and malformed/legacy metadata handling.
Changes:
- Persist effective capability mode in the Project node (
properties.index_mode) and use exact JSON string comparisons when reading it. - Route mode upgrades to full rebuilds; keep stored effective mode for incremental changed-file re-extraction on downgrades.
- Refresh existing artifacts after reindexing (FAST quality unless persistence is explicitly requested) and add end-to-end tests for these transitions.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_pipeline.c | Adds regression tests covering upgrade/downgrade routing, capability preservation, malformed metadata, and artifact refresh behavior. |
| src/store/store.h | Clarifies that index_mode in coverage metadata is the requested discovery mode; effective capability mode is stored on the Project node. |
| src/pipeline/pipeline.c | Persists Project index_mode, reads stored mode safely via yyjson, routes upgrades to rebuilds, and adjusts post-publish artifact export behavior. |
| src/pipeline/pipeline_internal.h | Exposes cbm_pipeline_mode_name for consistent internal serialization and updates incremental API contract docs/signature. |
| src/pipeline/pipeline_incremental.c | Accepts an effective_mode for changed-file re-extraction, preserves macro extraction capability on downgrades, and propagates persistence failures when required. |
| src/mcp/mcp.c | Updates MCP tool schema description to document artifact refresh behavior when an artifact already exists. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
17cf892 to
902262e
Compare
|
Thank you for the contribution and for covering both index mode upgrades and downgrades, including malformed metadata. This is now triaged as a high-priority graph correctness bug for |
902262e to
52866c1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/pipeline/pipeline.c:1527
- Assigning the stored mode to
p->modehere also changes discovery, becausecbm_pipeline_run_stagedlater passesp->modetocbm_discover_ex2. Afull → fastdowngrade therefore still scans full-only directories/files instead of using the requested discovery scope, contradicting the stated requested/effective-mode separation and defeating the fast-mode workaround for large repositories. Keep the requested discovery mode separate and use the stored mode only for effective extraction/post-passes and persisted capability metadata.
p->mode = stored_mode;
src/pipeline/pipeline_incremental.c:2426
- This exports the artifact during the inner staged run, but the successful return is then marked incremental and
export_after_publishexports it again (BEST quality for explicit persistence, FAST for an existing artifact). Thus every exact no-op that needs an artifact performs compression/VACUUM twice, with the first export occurring before final publication. Let the existing post-publication path perform the single export.
return cbm_pipeline_refresh_artifact(p, db_path);
Signed-off-by: Anton Standrik <astandrik@yandex-team.ru>
Signed-off-by: Anton Standrik <astandrik@yandex-team.ru>
ef0d3e9 to
afb88f5
Compare
Summary
Fixes #1273
Related: #563
index_modeRoot cause
Incremental routing originally considered stored file hashes and the discovered-file threshold, but not the capability level of the stored index. This caused unchanged
fast → moderateupgrades to take the incremental no-op path.Changed-file downgrades re-extracted with the weaker requested mode, which could remove stored capabilities such as
SIMILAR_TOedges or full-modeMacronodes.The metadata parser also compared yyjson strings with
strcmp(). A valid JSON string containing"full" + U+0000 + "garbage"therefore matched"full"at the embedded NUL and could incorrectly select incremental/no-op instead of the conservative rebuild route.Mode transitions
fastmoderate/fullmoderatefullDowngrades keep the stored
index_mode;p->modeand Project metadata are not weakened. Files outside requested discovery continue to use the existing mode-skipped/hash mechanism. A no-change run leaves the on-disk DB untouched. If a persistent artifact already exists, any reindex refreshes it after publication — at FAST quality unless persistence is explicitly requested, so the artifact encoding no longer oscillates with the route and full rebuilds no longer pay zstd-9 + double VACUUM by default.The public MCP API and SQLite schema are unchanged. Existing changed-file threshold and ADR preservation behavior are unchanged.
Regression coverage
fast → moderate: fail-before incremental no-op leftSIMILAR_TOat 0; pass-after rebuild creates similarity data and recordsindex_mode=moderatemoderate → fast: preservesSIMILAR_TOandindex_mode=moderate; the followingmoderateno-op remains stablefull → moderate: preserves full-onlyMacronodes andindex_mode=full; the followingfullno-op remains stable{}triggers a rebuild and restoresindex_modeindex_mode("full\^@garbage") is rejected by exactyyjson_equals_strcomparison, rebuilt once, and normalized to the requested modeVerification
PR head is
902262e7(single DCO commit, rebased onto currentmain).222 passed, 2 failedyyjson_equals_str):231 passed, 1 failed— the new test pins the exact-comparison requirementASAN_OPTIONS=detect_leaks=0 build/c/test-runner pipeline—232 passedmake -f Makefile.cbm lint-format lint-no-suppress— passedgit diff --checkagainstmain— passedscripts/test.shlocally: 6741 passed, 2 failed, 4 skipped (121 suites) — both failures are local macOS environment stalls (dyld hanging while exec'ing copied runtime images from /tmp, before any repo code runs; neitherdaemon_runtimenorindex_supervisoris touched by this diff)Review follow-up: the PR was slimmed from 1016 to 588 added lines (−42%) after a deep review pass. Removed periphery with no contract change: the byte-exact properties read API, the no-change downgrade coverage carry-forward, and the changed-file linear-scan fallback; metadata reads now go through the existing
cbm_store_find_node_by_qn.